/* ++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++

	PrintSample

		 PrintThread.cpp

			Functions used by the printing thread to send the 
			application's document content to the printer.

 ------------------------------------------------------------------

 Copyright (c) 2010 Microsoft Corporation. All rights reserved.

 THIS CODE AND INFORMATION IS PROVIDED "AS IS" WITHOUT WARRANTY OF
 ANY KIND, EITHER EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO
 THE IMPLIED WARRANTIES OF MERCHANTABILITY AND/OR FITNESS FOR A 
 PARTICULAR PURPOSE.

 ------------------------------------------------------------------
*/
#include "stdafx.h"
#include "PrintSample.h"

// Forward declaration of functions used within this module
HRESULT CreateFontResourceForFont( IXpsOMObjectFactory* xpsFactory, 
									LPCWSTR fontName,
									IXpsOMFontResource **fontResource);
HRESULT PrintStep_1_StartJob (PPRINTTHREADINFO threadInfo);
HRESULT PrintStep_2_DoOnePackage (PPRINTTHREADINFO threadInfo);
HRESULT PrintStep_3_DoOneDoc (PPRINTTHREADINFO threadInfo);
HRESULT PrintStep_4_DoOnePage (PPRINTTHREADINFO threadInfo);
HRESULT PrintStep_5_ClosePackage (PPRINTTHREADINFO threadInfo);
HRESULT PrintStep_6_CloseJob (PPRINTTHREADINFO threadInfo);

HRESULT AddContentToPage (PPRINTTHREADINFO threadInfo, 
									IXpsOMPage *xpsPage);
HRESULT CreateFontResourceForFont(IXpsOMObjectFactory* xpsFactory, 
									LPCWSTR fontName, 
									IXpsOMFontResource **fontResource);
HRESULT AddGlyphs(IXpsOMObjectFactory* xpsFactory, 
									IXpsOMPage* page, 
									IXpsOMFontResource* font, 
									LPCWSTR text, 
									XPS_POINT *origin, 
									FLOAT fontSize, 
									XPS_STYLE_SIMULATION sims);

DWORD WINAPI ClosePackageWriterThreadProc( LPVOID lpParam );
/** 
	<module>PrintThread.cpp</module>

	<summary>
		Entry point for thread that sends the application content 
		to the printer.</summary>
 
	<param name="lpParam">
		Pointer to the application's data structure.</param>

	<returns>
		ERROR_SUCCESS</returns>

*/
DWORD WINAPI 
PrintThreadProc( 
	LPVOID lpParam 
)
{
	PPRINTTHREADINFO	threadInfo = NULL;
	DWORD				waitStatus = ERROR_SUCCESS;
	DWORD				fnStatus = ERROR_SUCCESS;
	HRESULT				hr = S_OK;

	// Note that the step delay is to delay processing
	//  in order to make it easier to cancel the print job 
	//  in this demonstation. This value should be 0 in 
	//  production code
	DWORD				stepDelay = 0L;
	DWORD				pagesPerStep = 1L;

	// Get the pointer to the application's data
	//  that contains the information about the document
	//  to print
	threadInfo = (PPRINTTHREADINFO)lpParam;

	// If there's no data, then there's nothing to print.
	//  Exit this function to terminate the thread
	if (NULL == threadInfo) 
	{
		return ERROR_SUCCESS;
	}

	// Set the thread priority so as to not interfere with the 
	//  UI (dialog box) thread. As a rule, UI threads should have
	//  a higher priority than processing threads so the UI can reamain
	//  responsive. If a processing thread has a higher priority than
	//  the UI thread and is performing a lengthy task, the UI can 
	//  become unresponsive and the applicatin can appear hung to
	//  the user.
	SetThreadPriority (GetCurrentThread(), THREAD_PRIORITY_LOWEST);

	// Adjust step delay value for document size
	//  to make it easier to cancel the print job in this
	//  demonstation. 

	// **************************************************
	//  This delay is only for demonstration. There should 
	//  be no artificial delays added to production code.
	// **************************************************

	stepDelay = 1000 / 
		(threadInfo->documentContent->pages * 
			threadInfo->copies * 
			threadInfo->packages);

	if (stepDelay < SHORT_WAIT) stepDelay = SHORT_WAIT;

	// Set the current copy of the document being printed.
	threadInfo->currentCopy = 1;

	// Create the completion event used by the print spooler
	//  to indicate when the print job has completed.
    threadInfo->completionEvent = CreateEvent(NULL, TRUE, FALSE, NULL);
    if (!threadInfo->completionEvent)
    {
        hr = HRESULT_FROM_WIN32(GetLastError());
    }

	// If no errors at this point, create the XPS factory that will 
	//  be used to create the XPS API interfaces.
    if (SUCCEEDED(hr))
	{
		hr = CoCreateInstance(
			__uuidof(XpsOMObjectFactory), 
			NULL,
			CLSCTX_INPROC_SERVER, 
			IID_PPV_ARGS(&threadInfo->xpsFactory));
	}

	// If no errors at this point, check to see if the user has
	//  clicked cancel.
    if (SUCCEEDED(hr))
	{
		waitStatus = WaitForSingleObject (
						threadInfo->quitEvent, 
						stepDelay);
		if (WAIT_OBJECT_0 == waitStatus)
		{
			hr = E_ABORT;
		}
	}

	// If no errors at this point, start the print job.
    if (SUCCEEDED(hr))
	{
		// Do 1 printing step
		hr = PrintStep_1_StartJob (threadInfo);

		// Update print status
		PostMessage (
			threadInfo->parentWindow, 
			USER_PRINT_STATUS_UPDATE, 
			0L, 
			0L);
	}

	// While no errors and the user hasn't clicked cancel...
	while (((waitStatus = WaitForSingleObject (
							threadInfo->quitEvent, 
							stepDelay)) == WAIT_TIMEOUT) &&
			SUCCEEDED(hr))
	{
		// ...print one package
		hr = PrintStep_2_DoOnePackage (threadInfo);

		// Update print status
		PostMessage (
			threadInfo->parentWindow, 
			USER_PRINT_STATUS_UPDATE, 
			0L, 
			0L);

		// While no errors and the user hasn't clicked cancel...
		while (((waitStatus = WaitForSingleObject (
								threadInfo->quitEvent, 
								stepDelay)) == WAIT_TIMEOUT) &&
				SUCCEEDED(hr))
		{
			// ...print one document
			hr = PrintStep_3_DoOneDoc (threadInfo);

			// Update print status
			PostMessage (threadInfo->parentWindow, USER_PRINT_STATUS_UPDATE, 0L, 0L);

			// Set the page being printed
			threadInfo->currentPage = 1;

			// While no errors and the user hasn't clicked cancel...
			while (((waitStatus = WaitForSingleObject (
									threadInfo->quitEvent, 
									stepDelay)) == WAIT_TIMEOUT) &&
					SUCCEEDED(hr))
			{
				// ...print one page
				hr = PrintStep_4_DoOnePage (threadInfo);

				// Update print status
				PostMessage (
					threadInfo->parentWindow, 
					USER_PRINT_STATUS_UPDATE, 
					0L, 
					0L);
				

				if (threadInfo->currentPage < (threadInfo->documentContent)->pages)
				{
					// More pages, so ontinue to the next one
					threadInfo->currentPage++;
				}
				else
				{
					// Last page printed so exit loop and close
					break;
				}
			}

			// Document is closed when new one is started
			//  or when package is closed. There is no
			//  separate "close document" method.

			if (threadInfo->currentCopy < threadInfo->copies)
			{
				// More copies, so continue to the next copy.
				threadInfo->currentCopy += 1;
			}
			else
			{
				// No more copies, so break out of this loop.
				break;
			}
		}

		// More packages?
		threadInfo->currentPackage += 1;
		if (threadInfo->currentPackage < threadInfo->packages)
		{
			// Send progress message
			PostMessage (threadInfo->parentWindow, USER_PRINT_STATUS_UPDATE, 0L, 0L);

			// Yield the thread to allow for display updating
			Sleep(0);

			// Close this package and...
			hr = PrintStep_5_ClosePackage (threadInfo);		
			// ...continue to the next package
		}
		else
		{
			// Send closing message
			PostMessage (threadInfo->parentWindow, USER_PRINT_CLOSING, 0L, (LPARAM)hr);

			// Yield the thread to allow for display updating
			Sleep(0);

			// Close this package and...
			hr = PrintStep_5_ClosePackage (threadInfo);		
			// ...leave loop to close print job
			break;
		}
	}

	// Indicate print job completion status to parent
	if (FAILED(hr))
	{
		// Job had an error.
		fnStatus = hr;
	} 
	else if (WAIT_TIMEOUT != waitStatus) 
	{
		// Job was cancelled by user.
		fnStatus =  ERROR_CANCELLED;
	}
	else
	{
		// Job finished. hr indicates success.
	}

	// Always call the close step to free any allocated resources
	hr = PrintStep_6_CloseJob (threadInfo);

	// Close and release remaining resources
    if (NULL != threadInfo->xpsFactory)
    {
        (threadInfo->xpsFactory)->Release();
        threadInfo->xpsFactory = NULL;
    }
	
	if (NULL != threadInfo->completionEvent)
    {
        CloseHandle(threadInfo->completionEvent);
        threadInfo->completionEvent = NULL;
    }

	// Post job complete message to parent
	PostMessage (threadInfo->parentWindow, USER_PRINT_COMPLETE, 0L, LPARAM(fnStatus));

	// Thread function return value (this is not used)
	return ERROR_SUCCESS;
}

/** 
	<module>PrintThread.cpp</module>

	<summary>
		Starts the print job.</summary>
 
	<param name="lpParam">
		Pointer to the application's data structure.</param>

	<returns>
		HRESULT value that indicates the success or failure of the function.</returns>

*/
HRESULT 
PrintStep_1_StartJob (
	PPRINTTHREADINFO threadInfo
)
{
	// Start print job.
	return StartXpsPrintJob (
            threadInfo->printerName,
            NULL,
            NULL,
            NULL,
            threadInfo->completionEvent,
            NULL,
            0,
            &threadInfo->printJob,
            &threadInfo->printJobStream,
            NULL
            );
}

/** 
	<module>PrintThread.cpp</module>

	<summary>
		Creates a new package to send to the print job stream.</summary>
 
	<param name="lpParam">
		Pointer to the application's data structure.</param>

	<returns>
		HRESULT value that indicates the success or failure of the function.</returns>

*/
HRESULT 
PrintStep_2_DoOnePackage (
	PPRINTTHREADINFO threadInfo
)
{
	HRESULT hr = S_OK;
	IOpcPartUri* partUri = NULL;

	// Create the part name to use for the package part in the
	//  XPS document being sent to the printer.
    if (SUCCEEDED(hr))
    {
        hr = (threadInfo->xpsFactory)->CreatePartUri(
			// This is the part name used by an XPS document
			// for the FixedDocumentSequence. Do not change it.
			L"/FixedDocumentSequence.fdseq", 
			&partUri);
    }

	// Create the package writer that will send the document parts
	//  to the printer.
	if (SUCCEEDED(hr))
    {
        hr = (threadInfo->xpsFactory)->CreatePackageWriterOnStream(
                    threadInfo->printJobStream,
                    TRUE,
                    XPS_INTERLEAVING_ON,
                    partUri,
                    NULL,
                    NULL,
                    NULL,
                    NULL,
                    &threadInfo->packageWriter
                    );
    }

	// Release allocated resources.
    if (partUri)
    {
        partUri->Release();
        partUri = NULL;
    }

	return hr;
}

/** 
	<module>PrintThread.cpp</module>

	<summary>
		Sends a fixed document structure to the print job stream.</summary>
 
	<param name="lpParam">
		Pointer to the application's data structure.</param>

	<returns>
		HRESULT value that indicates the success or failure of the function.</returns>

*/
HRESULT 
PrintStep_3_DoOneDoc (
		PPRINTTHREADINFO threadInfo
)
{
	HRESULT hr = S_OK;
	IOpcPartUri* partUri = NULL;
	WCHAR	docPartName[MAX_PATH];

	// Format the part name for this document in the package.
	//  The format shown here is the format that is used by
	//  XPS documents. DO NOT CHANGE IT.
	if (swprintf_s (
		docPartName,
		MAX_PATH,
		L"/Documents/%d/FixedDocument.fdoc",
		threadInfo->currentCopy) < 0)
	{
		// Could not create the part name
		hr = E_FAIL;
	}

	// Create the part URI for this document.
	if (SUCCEEDED(hr))
    {
        hr = (threadInfo->xpsFactory)->CreatePartUri(docPartName, &partUri);
    }

	// Create the new document in the package writer.
    if (SUCCEEDED(hr))
    {
        hr = (threadInfo->packageWriter)->StartNewDocument(
				partUri, 
				NULL,
				NULL,
				NULL,
				NULL);
    }

	// Free the resources used.
    if (partUri)
    {
        partUri->Release();
        partUri = NULL;
    }

	return hr;
}

/** 
	<module>PrintThread.cpp</module>

	<summary>
		<p>Creates a new page object.</p>
		<p>Formats the application data for the page.</p>
		<p>Sends the formatted page to the print job stream.</p>
		</summary>
 
	<param name="lpParam">
		Pointer to the application's data structure.</param>

	<returns>
		HRESULT value that indicates the success or failure of the function.</returns>

*/
HRESULT 
PrintStep_4_DoOnePage (
		PPRINTTHREADINFO threadInfo
)
{
	HRESULT hr = S_OK;
	IOpcPartUri* partUri = NULL;
    XPS_SIZE pageSize = {};
    IXpsOMPage* xpsPage = NULL;
	WCHAR	pagePartName[MAX_PATH];

	// Get the page size fromt he application data
	pageSize = threadInfo->
				documentContent->
					pageContentArray[threadInfo->currentPage-1].pageSize;

	// Format the part name for this page in the document.
	//  The format shown here is the format that is used by
	//  XPS documents. DO NOT CHANGE IT.
	if (swprintf_s (
		pagePartName,
		MAX_PATH,
		L"/Documents/%d/Pages/%d.fpage",
		threadInfo->currentCopy,
		threadInfo->currentPage) < 0)
	{
		hr = E_FAIL;
	}

	// Create a part URI for this page.
    if (SUCCEEDED(hr))
    {
        hr = (threadInfo->xpsFactory)->CreatePartUri(
			pagePartName, 
			&partUri);
    }

	// Create the page object.
    if (SUCCEEDED(hr))
    {
        hr = (threadInfo->xpsFactory)->CreatePage(
			&pageSize, 
			L"en-US", 
			partUri, 
			&xpsPage);
    }

	// Add the application content for the current page
	//  to the page object.
	if (SUCCEEDED(hr))
	{
		hr = AddContentToPage (threadInfo, xpsPage);
	}

	// Add the completed page object to the package writer.
    if (SUCCEEDED(hr))
    {
        hr = (threadInfo->packageWriter)->AddPage(
                    xpsPage,
                    &pageSize,
                    NULL,
                    NULL,
                    NULL,
                    NULL);
    }

	// Once the page has been written to the package writer
	//  it can be released.
    if (NULL != xpsPage)
    {
        xpsPage->Release();
        xpsPage = NULL;
    }

	// ...and the part URI can also be released.
    if (partUri)
    {
        partUri->Release();
        partUri = NULL;
    }

	return hr;
}


/** 
	<module>PrintThread.cpp</module>

	<summary>
		Closes the package object.
		</summary>
 
	<param name="lpParam">
		Pointer to the application's data structure.</param>

	<returns>
		HRESULT value that indicates the success or failure of the function.</returns>

*/
HRESULT 
PrintStep_5_ClosePackage (
		PPRINTTHREADINFO threadInfo
)
{
	HRESULT hr = S_OK;
	XPS_JOB_STATUS jobStatus = {0};

	// Release the font used in this print job.
	if (NULL != threadInfo->fontResource)
	{
		(threadInfo->fontResource)->Release();
		threadInfo->fontResource = NULL;
	}

	// Close the package writer in a separate thread because
	//  it can tie things up while it does what it does.
	if (NULL != (threadInfo->packageWriter))
	{
		HANDLE	closeThread = NULL;
		DWORD	status = ERROR_SUCCESS;

		closeThread = CreateThread (NULL, 0L, ClosePackageWriterThreadProc, 
			(LPVOID)threadInfo->packageWriter, 0L, NULL);
		
		// Then wait for it to finish before contining
		status = WaitForSingleObject (closeThread, LONG_WAIT);
		if (status != WAIT_OBJECT_0)
		{
			// the thread did not end properly.
			hr = E_FAIL;
		}
	}

	// Release the package writer.
	if (NULL != threadInfo->packageWriter)
	{
		(threadInfo->packageWriter)->Release();
		threadInfo->packageWriter = NULL;
	}

	return  hr;
}

/** 
	<module>PrintThread.cpp</module>

	<summary>
		Closes the print job.</summary>
 
	<param name="lpParam">
		Pointer to the application's data structure.</param>

	<returns>
		HRESULT value that indicates the success or failure of the function.</returns>

*/
HRESULT 
PrintStep_6_CloseJob (
		PPRINTTHREADINFO threadInfo
)
{
	HRESULT hr = S_OK;
	XPS_JOB_STATUS jobStatus = {};

	// Close the print job stream to indicate
	//  to the print spooler that it has received
	//  all the contents of this print job.
	if (NULL != (threadInfo->printJobStream))
	{
		hr = (threadInfo->printJobStream)->Close();
	}

	if (FAILED(hr))
	{
		// Unable to close it so cancel it, but only cancel the 
		// job if we succeeded in creating one in the first place.
		if (NULL != threadInfo->printJob)
		{
			// Tell the XPS Print API that we're giving up.  
			// Don't overwrite hr with the return from
			// this function.
			(threadInfo->printJob)->Cancel();
		}
	}
	else
	{
		// Wait for print spooler to indicate the job has finished
		if (WaitForSingleObject(threadInfo->completionEvent, LONG_WAIT) != WAIT_OBJECT_0)
		{
			// The print spooler never indicated that the job was complete.
			hr = HRESULT_FROM_WIN32(GetLastError());
		}
	}

	// Only check the print job status if the job completed successfully.
    if (SUCCEEDED(hr))
    {
		if (NULL != threadInfo->printJob)
		{
			hr = (threadInfo->printJob)->GetJobStatus(&jobStatus);
		}
		else
		{
			// no print job to check
			hr = E_FAIL;
		}
    }

	// Check the print job status if no errors at this point.
    if (SUCCEEDED(hr))
    {
        switch (jobStatus.completion)
        {
            case XPS_JOB_COMPLETED:
				hr = S_OK;
                break;

			case XPS_JOB_CANCELLED:
                hr = E_FAIL;
                break;

            case XPS_JOB_FAILED:
                hr = E_FAIL;
                break;

			default:
                hr = E_UNEXPECTED;
                break;
        }
    }

	// Release the print job stream.
    if (NULL != threadInfo->printJobStream)
    {
        (threadInfo->printJobStream)->Release();
        threadInfo->printJobStream = NULL;
    }

	// Release the print job interface.
    if (NULL != threadInfo->printJob)
    {
        (threadInfo->printJob)->Release();
        threadInfo->printJob = NULL;
    }

	return hr;

}

/** 
	<module>PrintThread.cpp</module>

	<summary>
		Adds the applications data for a page to the page object that 
		will be sent to the printer.</summary>
 
	<param name="threadInfo">
		A pointer to the application's dats structure.</param>

	<param name="xpsPage">
		A pointer to the IXpsOMPage interface that will receive the page content.</param>

	<returns>
		HRESULT value that indicates the success or failure of the function.</returns>

*/
HRESULT 
AddContentToPage (
		PPRINTTHREADINFO threadInfo, 
		IXpsOMPage *xpsPage
)
{
	HRESULT hr = S_OK;
	IOpcPartUri* partUri = NULL;
	LPWSTR fontName = NULL;
	XPS_POINT origin = {};

	// Get the font name from the resources
	if (0 == GetAllocedResourceString (
				threadInfo->applicationInstance, 
				IDS_FONT_NAME, 
				&fontName))
	{
		// unable to get font name from resource file.
		hr = E_FAIL;
	}

	if (SUCCEEDED(hr))
    {
		// Create font resource if it has not been created for this document
		if (NULL == threadInfo->fontResource) {
			hr = CreateFontResourceForFont(
				threadInfo->xpsFactory, 
				fontName, 
				&(threadInfo->fontResource));
		}
		// Free font name string buffer
		HeapFree (GetProcessHeap(), 0L, fontName);
    }

    if (SUCCEEDED(hr))
    {
		// Write each text element of this page to the page object
		DWORD t = 0;
		DWORD pageIndex = threadInfo->currentPage-1;

		for (t = 0; t < threadInfo->documentContent->pageContentArray[pageIndex].pageTextItems; t++)
		{
			PPAGETEXT pageText = &threadInfo->
									documentContent->
										pageContentArray[pageIndex].pageTextArray[t];
			hr = AddGlyphs(
					threadInfo->xpsFactory,
					xpsPage,
					threadInfo->fontResource,
					pageText->text,
					&pageText->textOrigin,
					pageText->fontSize,
					pageText->textStyle);

			if (FAILED(hr)) break; // Exit loop on error.
		}
    }
	
	return hr;
}

/** 
	<module>PrintThread.cpp</module>

	<summary>
		Creates a font resource for the given font name with the embedding 
		type set appropriately for the licensing intent of the font.
		</summary>
 
	<param name="xpsFactory">
		A pointer to a valid IXpsOMObjectFactory interface.</param>

	<param name="fontName">
		The name of the font to load in the created font resource.</param>

	<param name="fontResource">
		A pointer to the font resource created.</param>
		
	<returns>
		HRESULT value that indicates the success or failure of the function.</returns>

*/
HRESULT 
CreateFontResourceForFont(
		IXpsOMObjectFactory* xpsFactory, 
		LPCWSTR fontName, 
		IXpsOMFontResource **fontResource
)
{
    HRESULT			hr = S_OK;
    HDC				hDC = NULL;
    LOGFONT			logFont = {};
    HGDIOBJ			hFont = NULL;
    ULONG			privStatus = 0;
    LONG			ttStatus;
    XPS_FONT_EMBEDDING embedding = XPS_FONT_EMBEDDING_NORMAL;
    DWORD			fontDataLen = GDI_ERROR;
    HGLOBAL			hGlobal = NULL;
    LPVOID			ptr = NULL;
    IStream			*fontStream = NULL;
    GUID			fontObfuscationGuid = {};
    IOpcPartUri		*partUri = NULL;
    WCHAR			fontPartName[60];

	// Create a device context to use for this font.
    hDC = CreateDC(L"DISPLAY", NULL, NULL, NULL);

    if (!hDC)
    {
        hr = E_UNEXPECTED;
    }

	// Copy the fontname to the data structure
    if (SUCCEEDED(hr))
    {
        if (wcscpy_s(logFont.lfFaceName, fontName) != 0)
        {
            hr = E_UNEXPECTED;
        }
    }

	// Create the GDI font.
    if (SUCCEEDED(hr))
    {
        hFont = CreateFontIndirect(&logFont);
        if (!hFont)
        {
            hr = E_UNEXPECTED;
        }
    }

	// Select the font
    if (SUCCEEDED(hr))
    {
        if (!SelectObject(hDC, hFont))
        {
            hr = E_UNEXPECTED;
        }
    }

	// Get the font's embedding type.
    if (SUCCEEDED(hr))
    {
		ttStatus = TTGetEmbeddingType(hDC, &privStatus);
        if (ttStatus != E_NONE)
        {
            hr = E_UNEXPECTED;
        }
    }

	// Set the embedding value.
    if (SUCCEEDED(hr))
    {
        switch (privStatus)
        {
            case EMBED_PREVIEWPRINT:
                // Restricted font
                embedding = XPS_FONT_EMBEDDING_RESTRICTED;
                break;

			case EMBED_EDITABLE:
                // Editable font - MUST obfuscate
                embedding = XPS_FONT_EMBEDDING_OBFUSCATED;
                break;

			case EMBED_INSTALLABLE:
                // Installable font - SHOULD obfuscate
                embedding = XPS_FONT_EMBEDDING_OBFUSCATED;
                break;

			case EMBED_NOEMBEDDING:
                hr = E_UNEXPECTED;
                break;

			default:
                hr =  E_UNEXPECTED;
                break;
        }
    }

	// Get information about the font.
    if (SUCCEEDED(hr))
    {
        fontDataLen = GetFontData(hDC, 0, 0, NULL, 0);
        if (fontDataLen == GDI_ERROR)
        {
            hr = E_UNEXPECTED;
        }
    }

	// Allocate a buffer for the font data
    if (SUCCEEDED(hr))
    {
        hGlobal = GlobalAlloc(GMEM_MOVEABLE, fontDataLen);
        if (NULL == hGlobal)
        {
            hr = E_OUTOFMEMORY;
        }
    }

	// Lock the allocated buffer
	if (SUCCEEDED(hr))
    {
        ptr = GlobalLock(hGlobal);
        if (NULL == ptr)
        {
            hr = HRESULT_FROM_WIN32(GetLastError());
        }
	}
		// Lock the allocated buffer
	if (SUCCEEDED(hr))
    {
        if (GetFontData(hDC, 0, 0, ptr, fontDataLen) == GDI_ERROR)
        {
            hr = E_UNEXPECTED;
        }
        GlobalUnlock(hGlobal);
    }

    // Could perform font subsetting with CreateFontPackage here.

    if (SUCCEEDED(hr))
    {
		// Create font stream
        hr = CreateStreamOnHGlobal(hGlobal, TRUE, &fontStream);
    }

	if (SUCCEEDED(hr))
    {
        // If CreateStreamOnHGlobal succeeded, the stream now controls the lifetime of the
        // HGLOBAL.
        hGlobal = NULL;
    }

	// Create a GUID to use for obfuscating the font
    if (SUCCEEDED(hr))
    {
        hr = CoCreateGuid(&fontObfuscationGuid);
    }

	// Create the obfuscated font name
    if (SUCCEEDED(hr))
    {
        if (swprintf_s(
                fontPartName,
                sizeof(fontPartName)/sizeof(WCHAR),
                L"/Resources/Fonts/%08lX-%04X-%04X-%02X%02X-%02X%02X%02X%02X%02X%02X.odttf",
                fontObfuscationGuid.Data1,
                fontObfuscationGuid.Data2,
                fontObfuscationGuid.Data3,
                fontObfuscationGuid.Data4[0],
                fontObfuscationGuid.Data4[1],
                fontObfuscationGuid.Data4[2],
                fontObfuscationGuid.Data4[3],
                fontObfuscationGuid.Data4[4],
                fontObfuscationGuid.Data4[5],
                fontObfuscationGuid.Data4[6],
                fontObfuscationGuid.Data4[7]
                ) == -1)
        {
            hr = E_UNEXPECTED;
        }
    }

    if (SUCCEEDED(hr))
    {
        hr = xpsFactory->CreatePartUri(fontPartName, &partUri);
    }

	// Create the new font resource.
    if (SUCCEEDED(hr))
    {
        hr = xpsFactory->CreateFontResource(
                fontStream,
                embedding,
                partUri,
                FALSE,
                fontResource);
    }

	// Free resources.
    if (fontStream)
    {
        fontStream->Release();
        fontStream = NULL;
    }

    if (partUri)
    {
        partUri->Release();
        partUri = NULL;
    }

    if (hGlobal)
    {
        GlobalFree(hGlobal);
    }

    if (hFont)
    {
        DeleteObject(hFont);
    }

    if (hDC)
    {
        DeleteDC(hDC);
    }

    return hr;
}

/** 
	<module>PrintThread.cpp</module>

	<summary>
		Writes a text string as glyph objects to a page.</summary>
 
	<param name="xpsFactory">
		A pointer to a valid IXpsOMObjectFactory interface.</param>

	<param name="page">
		A pointer to the IXpsOMPage interface that will receive the text.</param>

	<param name="font">
		The font resource to use for the text string.</param>
		
	<param name="text">
		The text string to write.</param>
		
	<param name="origin">
		The starting location of the text on the page.</param>
		
	<param name="fontSize">
		The size of the font to use for the text.</param>
		
	<param name="sims">
		The font style simulation to use for the text.</param>
		
	<returns>
		HRESULT value that indicates the success or failure of the function.</returns>

*/
HRESULT 
AddGlyphs(
		IXpsOMObjectFactory* xpsFactory, 
		IXpsOMPage* page, 
		IXpsOMFontResource* font,
		LPCWSTR text, 
		XPS_POINT *origin, 
		FLOAT fontSize, 
		XPS_STYLE_SIMULATION sims
)
{
    HRESULT hr = S_OK;

    IXpsOMGlyphs* glyphs = NULL;
    IXpsOMGlyphsEditor* glyphsEditor = NULL;
    IXpsOMSolidColorBrush* solidColorBrush = NULL;
    IXpsOMVisualCollection* visuals = NULL;

	// Create Glyphs interface.
    hr = xpsFactory->CreateGlyphs(
                font,
                &glyphs);

	// Create GlyphsEditor interface to manipulate
	// the properties of the Glyphs interface.
    if (SUCCEEDED(hr))
    {
        hr = glyphs->GetGlyphsEditor(&glyphsEditor);
    }

	// Set the text of this glyph run.
    if (SUCCEEDED(hr))
    {
        hr = glyphsEditor->SetUnicodeString(text);
    }

    if (SUCCEEDED(hr))
    {
        hr = glyphsEditor->ApplyEdits();
    }

	// Set the text color.
	//  For this sample application, the text color is 
	//  always black (0,0,0);
    if (SUCCEEDED(hr))
    {
        XPS_COLOR color;
        color.colorType = XPS_COLOR_TYPE_SRGB;
        color.value.sRGB.alpha = 255;
        color.value.sRGB.red = 0;
        color.value.sRGB.green = 0;
        color.value.sRGB.blue = 0;

        hr = xpsFactory->CreateSolidColorBrush(
                    &color,
                    NULL,
                    &solidColorBrush);
    }

    if (SUCCEEDED(hr))
    {
        hr = glyphs->SetFillBrushLocal(solidColorBrush);
    }

	// Set the origin point of this glyph run.
    if (SUCCEEDED(hr))
    {
        hr = glyphs->SetOrigin(origin);
    }

	// Set the font size of this glyph run.
    if (SUCCEEDED(hr))
    {
        hr = glyphs->SetFontRenderingEmSize(fontSize);
    }

	// Set the text style.
    if (SUCCEEDED(hr))
    {
        hr = glyphs->SetStyleSimulations(sims);
    }

	// Get the visuals object for the page
    if (SUCCEEDED(hr))
    {
        hr = page->GetVisuals(&visuals);
    }

	// Append this glyph run to the list of visuals.
    if (SUCCEEDED(hr))
    {
        hr = visuals->Append(glyphs);
    }

	// Release the resources used in this function.
    if (visuals)
    {
        visuals->Release();
        visuals = NULL;
    }

    if (solidColorBrush)
    {
        solidColorBrush->Release();
        solidColorBrush = NULL;
    }

    if (glyphsEditor)
    {
        glyphsEditor->Release();
        glyphsEditor = NULL;
    }

    if (glyphs)
    {
        glyphs->Release();
        glyphs = NULL;
    }

    return hr;
}

/** 
	<module>PrintThread.cpp</module>

	<summary>
		Thread procedure that allows the close method of the 
		package writer to be called	from a separate thread to
		eliminate any blocking issues.</summary>
 
	<param name="lpParam">
		A pointer to the application's dats structure.</param>

	<returns>
		HRESULT value returned by the Close method.</returns>

*/
DWORD WINAPI ClosePackageWriterThreadProc( LPVOID lpParam ) 
{
	IXpsOMPackageWriter *packageWriter = NULL;
	HRESULT				hr = S_OK;

	packageWriter = (IXpsOMPackageWriter *)lpParam;
	
	if (NULL != packageWriter) {
		hr = packageWriter->Close();
	}
	return hr;
}